﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.IO;
using System.Data;

namespace EntitiesLayer
{   
    /// <summary>
    /// Clase para crear metodos de extencion utilitarios. Es parecido a lo que se puede hacer en javascript.
    /// </summary>
    public static class NuestrasExtenciones
    {
        /// <summary>
        /// Metodo de extencion que permite una busqueda binaria en un IEnumerable o subclase(IList, etc) 
        /// de una instancia IEntity por medio de su Id.
        /// Este metodo puede ser de especial utilidad ya que todos las consultas 
        /// tienen el Id de las entidades ordenado de menor a mayor.
        /// </summary>
        /// 
        /// <param name="list"> metodo que indica el tipo que se desea extender (No debe ser incluido en los 
        /// parametros de la invocacion)</param>
        /// 
        /// <param name="id"> id de la entidad que se desea buscar</param>
        /// <returns> El objeto IEntity con el id especificado o null en caso de no ser encontrado.</returns>
        public static IEntity BinaryEntityIdSearch(this IList<IEntity> list, int id)
        {
            return binarySearch(list, 0, list.Count-1, id);
        }


        private static IEntity binarySearch(IList<IEntity> list, int i, int f, int id){
            
            int mid= (i+f)/2;

            if(i>f)
                return null;


            if (list[mid].Id < id)
                return binarySearch(list, mid + 1, f, id);
            if (list[mid].Id > id)
                return binarySearch(list, i, mid - 1, id);

            return list[mid];
        }
        /// <summary>
        /// Metodo de extencion "Like" para tipos IEnumerable o subclases(IList, List etc). Para filtrar objetos 
        /// por medio de cualquier atributo que sea un string o cualquier tipo x.toString(). 
        /// Este metodo retorna un iterador con el uso de yield return por lo tanto es de naturaleza lazy
        /// </summary>
        ///
        /// <typeparam name="T"> T es un tipo generico. Al llamar este metodo no hay que especificarlo 
        /// ya que el compilador lo infiere</typeparam>
        /// <param name="lista"> Este parametro simplemente indica el tipo al cual se le va a agregar el metodo de extencion 
        /// este no debera se pasado como parametro. 
        /// (En este caso es un IEnumerableo o subclase, que contenga cualquier tipo)</param>
        ///
        /// <param name="type"> es una funcion que retorna el atributo por el cual se desea filtrar EJEMPLO: p=>p.Nombre </param>
        ///
        /// <param name="pattern">El patron que se desea buscar</param>
        /// <returns></returns>
        public static IEnumerable<T> Like<T>(this IEnumerable<T> lista, Func<T, string> type, string pattern)
        {

            if (pattern.Length > 0)
            {
                int[] pf = prefixFunction(pattern);

                foreach (T e in lista)
                {
                    if (patternKMP(pattern, type(e), pf))
                        yield return e;
                }
            }
        }

        private static int[] prefixFunction(string p)
        {
            Func<char,char> inv = char.ToUpperInvariant;

            int[] pf = new int[p.Length];
            int k = pf[0] = -1;


            for (int i = 1; i < p.Length; i++)
            {
                while (k > -1 && inv(p[k + 1]) != inv(p[i]))
                    k = pf[k];

                pf[i] = inv(p[k + 1]) == inv(p[i]) ? ++k : k;
            }
            return pf;

        }

        private static bool patternKMP(string p, string t, int[] pf)
        {

            Func<char, char> inv = char.ToUpperInvariant;

            for (int i = 0, k = -1; i < t.Length; i++)
            {

                while (k > -1 && inv(p[k + 1]) != inv(t[i]))
                    k = pf[k];

                if (inv(p[k + 1]) == inv(t[i]))
                    k++;
                
                if (k == p.Length - 1)
                    return true;    
            }

            return false;

        }

        public static byte[]  ToByte( this Bitmap bitmap){

            if (bitmap != null)
            {
                byte[] byteArray = new byte[0];
                using (MemoryStream stream = new MemoryStream())
                {
                    bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
                    stream.Close();

                    byteArray = stream.ToArray();
                }
                return byteArray;
            }

            return null;
        }

        public static Bitmap toBitMap(this byte[] obj) {


            if (obj != null && obj.Length > 0)
            {

                ImageConverter ic = new ImageConverter();
                Image img = (Image)ic.ConvertFrom(obj);
                Bitmap bitmap1 = new Bitmap(img);
                
                return bitmap1;
            }

            return null;
        
        }

        public static Dictionary<int, string> ToDictionary(this IEnumerable<INameable> lista) {

            Dictionary<int, string> dic = new Dictionary<int, string>();

            foreach (INameable n in lista)
                dic.Add(n.Id, n.Nombre);

            return dic.Count > 0 ? dic : null;
        }

        public static  T NullSaveValue<T>(this Lazy<T> lazy) where T : class
        {
            return lazy != null ? lazy.Value : null;
        }


        public static T SafeDBNull<T>(this object value)
        {
            T de = default(T);
            
            if (value == null)
                return de;

            if (value is string)
                return (T)Convert.ChangeType(value, typeof(T));

            return (value == DBNull.Value) ? de : (T)value;
        } 

       
    
    }
}
